/* Copyright 2023 Man Group Operations Limited
 *
 * Use of this software is governed by the Business Source License 1.1 included in the file licenses/BSL.txt.
 *
 * As of the Change Date specified in that file, in accordance with the Business Source License, use of this software will be governed by the Apache License, version 2.0.
 */

#pragma once

#include <cstdint>

namespace arcticdb {

    template<size_t size>
    struct UnsignedPromoteImpl;

    template <>
    struct UnsignedPromoteImpl<8> {
        using type = int64_t;
    };

    template <>
    struct UnsignedPromoteImpl<4> {
        using type = int64_t;
    };

    template <>
    struct UnsignedPromoteImpl<2> {
        using type = int32_t;
    };

    template <>
    struct UnsignedPromoteImpl<1> {
        using type = int16_t;
    };

    template<typename T>
    struct UnsignedPromote {
        using type = typename UnsignedPromoteImpl<sizeof(T)>::type;
    };

    template <typename T, typename U>
    struct Comparable {
        using left_type = typename std::conditional<
                std::is_signed_v<T> == std::is_signed_v<U>,
                std::common_type_t<T, U>,
                typename std::conditional<
                std::is_signed_v<T>, T, typename UnsignedPromote<T>::type
                >::type
                >::type;

        using right_type = typename std::conditional<
                std::is_signed_v<T> == std::is_signed_v<U>,
                std::common_type_t<T, U>,
                typename std::conditional<
                std::is_signed_v<U>, U, typename UnsignedPromote<U>::type
                >::type
                >::type;
    };

    template <>
    struct Comparable<double, double> {
        using left_type = double;
        using right_type = double;
    };

    template <>
    struct Comparable<double, float> {
        using left_type = double;
        using right_type = double;
    };

    template <>
    struct Comparable<float, double> {
        using left_type = double;
        using right_type = double;
    };

    template <>
    struct Comparable<float, float> {
        using left_type = float;
        using right_type = float;
    };

    template <>
    struct Comparable<double, uint64_t> {
        using left_type = double;
        using right_type = double;
    };

    template <>
    struct Comparable<uint64_t, double> {
        using left_type = double;
        using right_type = double;
    };

    template <>
    struct Comparable<float, uint64_t> {
        using left_type = float;
        using right_type = float;
    };

    template <>
    struct Comparable<uint64_t, float> {
        using left_type = float;
        using right_type = float;
    };

    template <>
    struct Comparable<uint64_t, uint64_t> {
        using left_type = uint64_t;
        using right_type = uint64_t;
    };

    template <typename T>
    struct Comparable<T, double> {
        using left_type = double;
        using right_type = double;
    };

    template <typename T>
    struct Comparable<double, T> {
        using left_type = double;
        using right_type = double;
    };

    template <typename T>
    struct Comparable<T, float> {
        using left_type = float;
        using right_type = float;
    };

    template <typename T>
    struct Comparable<float, T> {
        using left_type = float;
        using right_type = float;
    };

    template <typename T>
    struct Comparable<uint64_t, T> {
        using left_type = uint64_t;
        using right_type = typename std::conditional<std::is_signed_v<T>, int64_t, T>::type;
    };

    template <typename T>
    struct Comparable<T, uint64_t> {
        using left_type = typename std::conditional<std::is_signed_v<T>, int64_t, T>::type;
        using right_type = uint64_t;
    };
}